package com.fengye.common.utils.http;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;

/**
 * http 工具类
 * <a href="https://juejin.cn/post/6844903656165212174?searchId=20240624151140BAF8CB4B1731B92DA157">...</a>
 */
@Slf4j
public class HttpUtils {

    private static RestTemplate restTemplate;

    static {
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        // 连接超时
        simpleClientHttpRequestFactory.setConnectTimeout(30000);
        // 读取数据的超时
        simpleClientHttpRequestFactory.setReadTimeout(30000);
        restTemplate = new RestTemplate(simpleClientHttpRequestFactory);
    }

    /**
     * 发送 get 请求
     *
     * @param url 请求路径
     * @param headers 请求头
     * @param returnType 返回值类型
     * @param <T> 泛型
     * @return 结果
     */
    @Nullable
    public static <T> T get(String url, HttpHeaders headers, Class<T> returnType, Map<String, Object> params) throws RestClientException {
        if (headers == null) {
            headers = new HttpHeaders();
        }
        HttpEntity<Object> entity = new HttpEntity<>(headers);
        ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.GET, entity, returnType, params);
        if (response.getStatusCode() != HttpStatus.OK) {
            log.error("请求失败，url:{}, params:{}, response:{}", url, params, response);
            return null;
        }

        return response.getBody();
    }

    /**
     * 发送 get 请求
     *
     * @param url 请求路径
     * @param params 请求参数
     * @return 结果
     */
    @Nullable
    public static String get(String url, Map<String, Object> params) throws RestClientException {
        return get(url, null, String.class, params);
    }

    /**
     * 发送 get 请求
     *
     * @param url 请求路径
     * @return 结果
     */
    @Nullable
    public static String get(String url) throws RestClientException {
        return get(url, null, String.class, null);
    }

    /**
     * 发送 post 请求
     *
     * @param url 请求路径
     * @param headers 请求头
     * @param params 请求体
     * @param responseType 返回值类型
     * @param <T> 泛型
     * @return 结果
     */
    @Nullable
    public static <T> T post(String url, HttpHeaders headers, Map<String, Object> params, Class<T> responseType) throws RestClientException {
        if (headers == null) {
            headers = new HttpHeaders();
        }
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Map<String, Object>> entity = new HttpEntity<>(params, headers);
        ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.POST, entity, responseType);
        return response.getBody();
    }

    /**
     * 上传文件
     *
     * @param uploadUrl 上传路径
     * @param localFilePath 待上传的本地文件路径
     * @param params 参数
     * @param responseType 返回值类型
     * @param <T> 泛型
     * @return 结果
     */
    public static <T> T uploadFile(String uploadUrl, String localFilePath, MultiValueMap params, Class<T> responseType) {
        FileSystemResource file = new FileSystemResource(localFilePath);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        MultiValueMap<String, Object> bodyParams = new LinkedMultiValueMap<>();
        bodyParams.add("file", file);
        if (params != null && params.size() > 0) {
            bodyParams.addAll(params);
        }
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(bodyParams, headers);
        T response = restTemplate.postForObject(uploadUrl, requestEntity, responseType);

        return response;
    }

    /**
     * 文件下载
     *
     * @param url 下载链接
     * @param localFilePath 保存路径
     */
    public boolean downloadFile(String url, String localFilePath) throws RestClientException, InvalidPathException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

        HttpEntity<Void> requestEntity = new HttpEntity<>(headers);

        boolean result = false;
        try {
            ResponseEntity<Resource> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, Resource.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                Resource fileResource = response.getBody();
                assert fileResource != null;
                Files.copy(fileResource.getInputStream(), Paths.get(localFilePath), StandardCopyOption.REPLACE_EXISTING);
                log.info("Downloaded file to: " + localFilePath);
                result = true;
            } else {
                log.error("Failed to download file: " + response.getStatusCode());
            }
        } catch (RestClientException e) {
            e.printStackTrace();
            log.error("调用 HttpUtils.downloadFile ConnectException, url={}, headers={}, exception={} ", url, headers.toString(), e.getMessage());
        } catch (InvalidPathException e) {
            e.printStackTrace();
            log.error("调用 HttpUtils.downloadFile ConnectException, url={}, headers={}, exception={} 数据流拷贝到本地失败", url, headers.toString(), e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            log.error("调用 HttpUtils.downloadFile ConnectException, url={}, headers={}, exception={} 获取数据流异常", url, headers.toString(), e.getMessage());
        }
        return result;
    }

    public static void main(String[] args) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("json", true);
        String s = get("https://whois.pconline.com.cn/ipJson.jsp?json={json}", params);
        System.out.println(s);
    }
}
